# -*- coding: utf-8 -*-
# @Time    : 2024/6/17 15:37
# @Author  : yujiahao
# @File    : 27_class.py
# @description:类空间


# 一.Python 类的空间问题


'''类的属性不仅可以在类内部添加，还可以在类的外部添加。'''


class A:
    # __init__里面可以添加类的属性
    def __init__(self, name):
        self.name = name

    def func(self, sex):
        self.sex = sex

    def func1(self):
        A.bbb = 'ccc'


# 1.1 添加类的对象属性

# 类外面可以：
obj = A('yujiahao')
obj.age = 18
print(obj.__dict__)

# 类内部也可以：
obj = A('yujiahao')  # __init__方法可以。
obj.func('男')  # func 方法也可以。
print(obj.__dict__)

# 1.2 添加类的静态属性


# 类的外部可以添加

A.aaa = 'llll'
print(A.__dict__)

# 类的内部也可以添加。

A.func1(111)
print(A.__dict__)

# 1.3 对象如何找到类的属性

'''

Python 对象实例化的过程包括以下步骤：
    1、调用类的构造函数：创建对象。
    2、分配内存空间：为对象分配内存。
    3、创建对象空间：在内存中创建对象空间，包含类对象指针和实例属性空间。
    4、调用 __init__ 方法：初始化对象属性。
    5、返回实例对象：构造函数返回新对象实例。



当你访问一个对象的属性时，Python 会按照以下顺序查找：

    1、实例属性：首先查找对象自身的实例属性。
    2、类属性：如果实例属性中没有找到，则查找类属性。
    3、父类属性：如果类属性中也没有找到，则继续查找父类的属性（如果有继承关系）。
    4、报错：如果在以上步骤中都没有找到属性，则抛出 AttributeError。

    如下例子：
    
        class Human:
            mind = '有思想'  # 类属性
            language = '实用语言'  # 类属性
        
            def __init__(self, name, sex):
                self.name = name  # 实例属性
                self.sex = sex  # 实例属性
        
            def display_info(self):
                print(f'Name: {self.name}, Sex: {self.sex}')
                print(f'Mind: {self.mind}, Language: {self.language}')
        
        # 创建对象
        obj = Human('barry', '男')
        
        # 访问实例属性
        print(obj.name)  # 输出: barry
        print(obj.sex)  # 输出: 男
        
        # 访问类属性
        print(obj.mind)  # 输出: 有思想
        print(obj.language)  # 输出: 实用语言
        
        # 通过类访问类属性
        print(Human.mind)  # 输出: 有思想
        print(Human.language)  # 输出: 实用语言
        
        # 调用方法
        obj.display_info()
        # 输出:
        # Name: barry, Sex: 男
        # Mind: 有思想, Language: 实用语言

总的来说：
    
    对象查找属性的顺序：先从对象空间找 ------> 类空间找 ------> 父类空间找 ------->.....
    
    类名查找属性的顺序：先从本类空间找 -------> 父类空间找--------> ........
    
    顺序都是单向不可逆，类名不可能找到对象的属性。

'''

# todo 二. 类与类之间的关系

'''
⼤千世界, 万物之间皆有规则和规律. 我们的类和对象是对⼤千世界中的所有事物进⾏归类. 那事物之间存在着相对应的关系. 
类与类之间也同样如此. 在⾯向对象的世界中. 类与类中存在以下关系:
    依赖关系
    组合关系
    继承关系(类的三大特性之一：继承。)
'''

# todo 2.1 依赖关系
'''

⾸先, 我们设计⼀个场景. 夏天到了大象很热,大象想到冰箱中. 注意. 在这个场景中, 其实是存在了两种事物的.
⼀个是⼤象, ⼤象负责整个事件的掌控者,
还有⼀个是冰箱, 冰箱负责被⼤象操纵. 通过这个我们要分出主次, 大象就是主 冰箱就是次

⾸先, 写出两个类, ⼀个是⼤象类, ⼀个是冰箱类

　　冰箱的功能非常简单, 只要会开⻔, 关⻔就⾏了. 但是⼤象就没那么简单了. ⼤象开⻔和关⻔的时候是不是要先找个冰箱. 
然后打开冰箱⻔. 是不是打开刚才找到的那个冰箱⻔. 然后装⾃⼰. 最后关冰箱⻔, 
注意, 关的是刚才那个冰箱. 也就是说. 开⻔和关⻔⽤的是同⼀个冰箱. 并且⼤象有更换冰箱的权利, 
想进那个冰箱就进那个冰箱. 这时, ⼤象类和冰箱类的关系并没有那么的紧密. 因为⼤象可以指定任何⼀个冰箱. 
【将一个类名或对象当做参数传递给另一个函数被使用就是依赖关系】


'''


class Elphant:
    def __init__(self, name):
        self.name = name

    def open(self, obj1):
        '''
        开⻔

        '''
        print('大象要开门了，默念三声，开')
        obj1.open_door()

    def close(self):
        '''
        关⻔
        '''
        print('大象要关门了，默念三声，关')


class Refrigerator:

    def open_door(self):
        print("冰箱⻔被打开了")

    def close_door(self):
        print("冰箱⻔被关上了")


elphant1 = Elphant('大象')
haier = Refrigerator()
elphant1.open(haier)

# 2.2 组合关系
'''这个最简单. 也是最常⽤的⼀种关系. 比如. 男女朋友关系. 男⼈关联着女朋友. 女⼈关联着男朋友. 这种关系可以是互相的, 也可以是单⽅⾯的
【组合关系,将一个类的对象传递到另一个类的对象属性中】
'''


# 定义类

class Boy:
    def __init__(self, name, girlFriend=None):
        self.name = name
        self.girlFriend = girlFriend

    def have_a_diner(self):
        if self.girlFriend:
            print(f'{self.name} 和 {self.girlFriend.name} 一起晚饭')
        else:
            print('单身狗，吃什么饭')


class Girl:
    def __init__(self, name):
        self.name = name


# 实例(创建)对象

b = Boy('张三')
b.have_a_diner()  # 此时是单身狗

# 突然有一天，张三牛逼了
g = Girl('如花')  # 创建一个 Girl 类的实例
b.girlFriend = g  # 将 b.girlFriend 赋值为 Girl 类的实例
b.have_a_diner()  # 共进晚餐

# 实例(创建)wusir对象

# wusir 生下来就有女朋友 服不服
gg = Girl('小花')
bb = Boy('wusir', gg)
bb.have_a_diner()

# 后来，分了
bb.girlFriend = None
bb.have_a_diner()


# 看个例子： 设计也给游戏角色，使用武器互殴，角色类  和 武器类


class Gamerole:
    def __init__(self, name, ad, hp):
        self.name = name
        self.ad = ad
        self.hp = hp

    def attack(self, p1):
        p1.hp -= self.ad
        print('%s攻击%s,%s掉了%s血,还剩%s血' % (self.name, p1.name, p1.name, self.ad, p1.hp))

    def equip_weapon(self, wea):
        self.wea = wea  # 组合：给一个对象封装一个属性改属性是另一个类的对象


class Weapon:
    def __init__(self, name, ad):
        self.name = name
        self.ad = ad

    def weapon_attack(self, p1, p2):
        p2.hp = p2.hp - self.ad - p1.ad
        print('%s 利用 %s 攻击了%s,%s还剩%s血'
              % (p1.name, self.name, p2.name, p2.name, p2.hp))


# 实例化三个人物对象：
meet = Gamerole('太白', 10, 200)
panky = Gamerole('金莲', 20, 50)
pillow = Weapon('绣花枕头', 2)

# 给人物装备武器对象。
meet.equip_weapon(pillow)

# 开始攻击
meet.wea.weapon_attack(meet, panky)
